import StorageKey from '@/constant/storage-key'

export interface Config {
    baseUrl?: string
    header?: Record<string, string>,
}

class Service {

    private config: Config = {
        baseUrl: '',
        header: {}
    }

    constructor(config: Config = {baseUrl: '', header: {}}) {
        this.config = config
    }

    async request<T>(url: string, method: string, payload?: Record<string, any>): Promise<T> {
        const response: Response = await fetch(new Request(url, {
            method: method,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'Authorization': localStorage.getItem(StorageKey.USER_TOKEN) || '',
                ...this.config?.header || {}
            },
            mode: 'cors',
            cache: 'no-cache',
            redirect: 'follow',
            credentials: 'same-origin',
            referrerPolicy: 'no-referrer',
            body: payload ? JSON.stringify(payload) : undefined
        }))

        if (response.ok) {
            return await response.json()
        } else {
            return Promise.reject(response.statusText)
        }
    }

    get<T = any>(path: string, parameter?: Record<string, any>): Promise<T> {
        const url: string = this.getFullUrl(path, parameter)

        return this.request(url, 'GET', undefined)
    }

    post<T = any>(path: string, payload?: Record<string, any> | Array<any>): Promise<T> {
        const url: string = this.getFullUrl(path)

        return this.request(url, 'POST', payload)
    }

    put<T = any>(path: string, payload?: Record<string, any>): Promise<T> {
        const url: string = this.getFullUrl(path)

        return this.request(url, 'PUT', payload)
    }

    delete<T = any>(path: string, parameter?: Record<string, any>, payload?: Record<string, any>): Promise<T> {
        const url: string = this.getFullUrl(path, parameter)

        return this.request(url, 'DELETE', payload)
    }

    setHeader(key: string, value: string): void {
        this.config.header = Object.assign(this.config.header || {}, {[key]: value})
    }

    setBaseUrl(url: string): void {
        this.config.baseUrl = url
    }

    getFullUrl(path: string, parameter?: Record<string, any>): string {
        const value: string = parameter ? this.serializeObject(parameter) : ''

        if (!this.isUrl(path) && this.config.baseUrl) {
            return this.config.baseUrl + path + value
        }

        return path + value
    }

    isUrl(url: string): boolean {
        const protocol = url.split('://')[0].toLowerCase()
        return protocol ? protocol.indexOf('http') > -1 : false
    }

    private serializeObject(object: Record<string, any>): string {
        if (object && Object.keys(object).length) {
            return `?${Object.keys(object).map(key => {
                const value = typeof object[key] === 'object' ? JSON.stringify(object[key]) : object[key]
                return `${key}=${encodeURIComponent(value)}`
            }).join('&')}`
        }
        return ''
    }
}

function Create(config: Config = {}) {
    return new Service(config)
}

export default {
    Service,
    Create
}
